/*
 * This file or a portion of this file is licensed under the terms of
 * the Globus Toolkit Public License, found in file ../GTPL, or at
 * http://www.globus.org/toolkit/download/license.html. This notice must
 * appear in redistributions of this file, with or without modification.
 *
 * Redistributions of this Software, with or without modification, must
 * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
 * some other similar material which is provided with the Software (if
 * any).
 *
 * Copyright 1999-2004 University of Chicago and The University of
 * Southern California. All rights reserved.
 */
package org.griphyn.vdl.directive;

import java.io.*;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.Iterator;
import java.util.Collection;
import java.util.MissingResourceException;
import edu.isi.pegasus.common.util.Separator;
import org.griphyn.vdl.parser.VDLxParser;
import org.griphyn.vdl.classes.*;
import org.griphyn.vdl.router.*;
import org.griphyn.vdl.dbschema.*;
import org.griphyn.vdl.dax.ADAG;

/**
 * This class generates the DAX per the request for LFNs or DVs
 *
 * @author Jens-S. Vöckler
 * @author Yong Zhao
 * @version $Revision: 2079 $
 *
 * @see org.griphyn.vdl.router.Route
 * @see org.griphyn.vdl.router.BookKeeper
 * @see org.griphyn.vdl.dbschema.VDC
 */
public class Explain extends Directive
{
  /**
   * Maintains the link to the VDC.
   */
  private DatabaseSchema m_dbschema = null;
 
  /** 
   * Provides a routing object to traverse dependencies in the VDC.
   */
  private Route m_route = null;

  /**
   * Helpful object for routing requests.
   */
  private BookKeeper m_state = null;

  /**
   * Constructor
   */
  public Explain()
    throws IOException, MissingResourceException
  { 
    super();
  }

  /**
   * Constructor: Sets the database schema instance, and 
   * initializes the internal <code>Route</code> accordingly.
   *
   * @param dbs   the database schema instance
   * @see org.griphyn.vdl.router.Route
   * @see org.griphyn.vdl.router.BookKeeper
   */
  public Explain(DatabaseSchema dbs)
    throws IOException, MissingResourceException
  {
    m_dbschema = dbs;
    m_route = new Route(m_dbschema);
    m_state = new BookKeeper();
  }

  /**
   * Sets database schema, and initialzes the internal
   * <code>Route</code> accordingly.
   *
   * @param dbs   the database schema instance
   * @see org.griphyn.vdl.router.Route
   * @see org.griphyn.vdl.router.BookKeeper 
   */
  public void setDatabaseSchema(DatabaseSchema dbs)
  {
    m_dbschema = dbs;
    m_route = new Route(m_dbschema);
    m_state = new BookKeeper();
  }

  /**
   * Allows to limit the maximum depth that the router is willing to go.
   *
   * @param depth is the maximum depth. Use Integer.MAX_VALUE for
   * (virtually) unlimited depth.
   */
  public void setMaximumDepth( int depth )
  {
    m_route.setMaximumDepth( depth );
  }

  /**
   * Requests a data product logical filename. As a result, the complete
   * build-style DAG for producing the requested filename will be
   * constructed.
   *
   * @param filename is the name of the requested LFN.
   * @return true if the request is successful
   *
   * @see #requestLFN( java.util.Collection )
   * @see org.griphyn.vdl.router.BookKeeper 
   */
  public boolean requestLFN( String filename )
    throws java.sql.SQLException
  {
    ArrayList al = new ArrayList(1);
    al.add(filename);
    return requestLFN(al);
  }

  /**
   * Requests a set of logical filenames. As a result, the complete
   * build-style DAG for producing the requested LFNs will be
   * constructed.
   *
   * @param filenames is the list or set of logical filenames requested.
   * @return true if the request is successful
   *
   * @see org.griphyn.vdl.router.Route#requestLfn( Collection, BookKeeper )
   * @see org.griphyn.vdl.router.BookKeeper 
   */
  public boolean requestLFN( java.util.Collection filenames ) 
    throws java.sql.SQLException
  {
    // FIXME: What about previous results? 
    m_route.requestLfn( filenames, m_state );
    return ( m_state != null && ! m_state.isEmpty() );
  }

  /**
   * Requests for a specific derivation. As a result, a build-style DAG
   * will be produced and maintained in the book-keeping structure.
   *
   * @param namespace is the namespace of the derivation.
   * @param name is the name of the derivation.
   * @param version is the version of the derivation.
   * @return true if the request is successful
   *
   * @see org.griphyn.vdl.router.Route#requestDerivation( String, String, String, BookKeeper )
   * @see org.griphyn.vdl.router.BookKeeper 
   */
  public boolean requestDerivation( String namespace, 
				    String name, 
				    String version )
  {
    return m_route.requestDerivation( namespace, name, version, m_state );
  }

  /**
   * Requests for a specific derivation. As a result, a build-style DAG
   * will be produced and maintained in the book-keeping structure.
   *
   * @param fqdn is the fully qualified name of the derivation.
   * @return true if the request is successful
   *
   * @see org.griphyn.common.util.Separator#splitFQDI( String )
   * @see org.griphyn.vdl.router.Route#requestDerivation( String, String, String, BookKeeper )
   * @see org.griphyn.vdl.router.BookKeeper 
   */
  public boolean requestDerivation( String fqdn )
    throws IllegalArgumentException
  {
    String[] name = Separator.splitFQDI(fqdn);
    return m_route.requestDerivation( name[0], name[1], name[2], m_state );
  }

  /**
   * Requests a set of specific derivations. As a result, a build-style
   * DAG will be produced and maintained in the book-keeping structure.
   *
   * @param symbolicList is a collecting of symbolic FQDN derivation triples.
   * @return true if the request is successful
   *
   * @see org.griphyn.common.util.Separator#splitFQDI( String )
   * @see org.griphyn.vdl.router.Route#requestDerivation( Collection, BookKeeper )
   * @see org.griphyn.vdl.router.BookKeeper 
   */
  public boolean requestDerivation( java.util.Collection symbolicList )
    throws IllegalArgumentException
  {
    return m_route.requestDerivation( symbolicList, m_state );
  }

  /**
   * Writes the abstract DAX from the accumulated results.
   *
   * @param writer  the output writer
   * @param label   the label of the dax
   *
   * @see org.griphyn.vdl.router.BookKeeper#getDAX
   */
  public void writeDAX( Writer writer, String label )
    throws IOException
  {
    this.writeDAX( writer, label, null );
  }

  /**
   * Writes the abstract DAX with a namespace prefix.
   *
   * @param writer  the output writer
   * @param label   the label of the dax
   * @param xmlns   the xml namespace prefix
   *
   * @see org.griphyn.vdl.router.BookKeeper#getDAX
   */
  public void writeDAX(Writer writer, String label, String xmlns)
    throws IOException
  {
    if ( m_state == null || m_state.isEmpty() ) {
      // whatever we did, there are no results for us
      m_logger.log( "explain", 0, "WARNING: The requested DAX is empty!\n" );
    } else {
      ADAG dax = m_state.getDAX( label == null ? "test" : label );
      m_logger.log( "explain", 2, "DAX has " +
		    dax.getFilenameCount() + " LFNs, " +
		    dax.getJobCount()  + " jobs, " + 
		    dax.getChildCount() + " deps." );
      dax.toXML(writer, "", xmlns);
    }
  }

  /**
   * Checks if the result is empty or not.
   *
   * @return true, if the result is undefined or empty, false otherwise.
   */
  public boolean isEmpty()
  {
    return ( m_state == null || m_state.isEmpty() );
  }
}

